package edu.asu.spring.quadriga.aspects;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import edu.asu.spring.quadriga.aspects.annotations.CheckAccess;
import edu.asu.spring.quadriga.aspects.annotations.CheckPublicAccess;
import edu.asu.spring.quadriga.domain.enums.EProjectAccessibility;
import edu.asu.spring.quadriga.domain.workbench.IProject;
import edu.asu.spring.quadriga.exceptions.AnnotationMisconfigurationException;
import edu.asu.spring.quadriga.service.workbench.IRetrieveProjectManager;
/**
* This aspect checks if the requested page can be accessed by the user.
* The aspect should be used only for controllers serving public pages. It requires
* that the project backing a page is already injected into the method. It will then
* check if the project is either public (then everybody can see the a page) or
* if the current user is authenticated with Quadriga and is either the owner of
* or a collaborator on the project.
*
* This aspect only applies to methods in the package 'edu.asu.spring.quadriga.web' that
* are annotated with {@link CheckPublicAccess}. The project parameter that should be checked for
* public access has to be annotated with {@link CheckAccess}.
*
* The aspect has to be executed after the {@link InjectProjectAspect}. Its order is 100.
*
* @author Julia Damerow
*
*/
@Aspect
@Order(value = 100)
@Component
public class PublicAccessAspect {
private static final Logger logger = LoggerFactory.getLogger(PublicAccessAspect.class);
@Autowired
private IRetrieveProjectManager projectManager;
@Around("within(edu.asu.spring.quadriga.web..*) && @annotation(check)")
public Object checkAccess(ProceedingJoinPoint joinPoint,
CheckPublicAccess check) throws Throwable {
// Get the Method signature and the arguments passed to the method.
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
Parameter[] paras = method.getParameters();
Object[] args = joinPoint.getArgs();
int projectIdx = -1;
Object projectObj = null;
// Loop through all the parameters and get the indices of the parameters
// with annotations CheckAccess.
if (paras != null) {
for (int i = 0; i < paras.length; i++) {
Parameter p = paras[i];
if (p.getAnnotation(CheckAccess.class) != null) {
projectIdx = i;
}
}
if (projectIdx == -1) {
throw new AnnotationMisconfigurationException("There is no parameter with a CheckAccess annotation.");
}
projectObj = args[projectIdx];
}
if (!(IProject.class.isAssignableFrom(projectObj.getClass()))) {
throw new AnnotationMisconfigurationException(
"The parameter you are accessing is not a project.");
}
IProject project = (IProject) projectObj;
Authentication auth = SecurityContextHolder.getContext()
.getAuthentication();
String userName = auth.getName().toLowerCase();
// if project is public or user can access the website because they are
// the project owner or a collaborator on the project, proceed. Otherwise
// show forbidden page.
if (project.getProjectAccess() == EProjectAccessibility.PUBLIC
|| (userName != null && projectManager.canAccessProjectWebsite(
project.getUnixName(), userName))) {
return joinPoint.proceed();
} else {
logger.debug("Access denied to: " + userName);
return "public/forbidden";
}
}
}